﻿using ALEXBlog.Common.Attributes;
using ALEXBlog.Common.EncryptionHelper;
using ALEXBlog.IService.Redis;
using Castle.DynamicProxy;
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace ALEXBlog.Extensions.Interceptors
{
    public class RedisInterceptor:IInterceptor
    {
        IRedisService redisContext;
        public RedisInterceptor(IRedisService redisService) 
        {
            redisContext = redisService;
        }

        /// <summary>
        /// 拦截器主体
        /// </summary>
        /// <param name="invocation"></param>
        public void Intercept(IInvocation invocation)
        {
            if (invocation.Arguments.Length > 10)
                throw new ArgumentException("请勿在超过10个参数情况下使用拦截器缓存");

            //获取拦截到的方法对象
            var method = invocation.MethodInvocationTarget ?? invocation.Method;
            //拦截到的方法如果无返回值(或为Task)则停止拦截并继续执行
            if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task)) 
            {
                invocation.Proceed();
                return;
            }

            //验证缓存特性
            var cacheAttribute = MethodCacheAttributeChacking(method);

            if (cacheAttribute is not null)
            {
                >>>
                var redisKey = CustomCacheKey(invocation);
                var redisKey = "";
                    var redisValue = redisContext.GetAsync(redisKey).Result;

                    //如果从redis获取到值
                    if (redisValue is not null)
                    {
                        //将当前获取到的缓存值，赋值给当前执行方法
                        Type returnType;

                        //返回类型是否可用转换为Task
                        if (typeof(Task).IsAssignableFrom(method.ReturnType))
                        {
                            returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault();
                        }
                        else
                        {
                            returnType = method.ReturnType;
                        }

                        //将从redis读取到的序列化的值进行相应类型的反序列化
                        dynamic result = Newtonsoft.Json.JsonConvert.DeserializeObject(redisValue, returnType);
                        invocation.ReturnValue = (typeof(Task).IsAssignableFrom(method.ReturnType)) ? Task.FromResult(result) : result;
                        return;
                    }

                    //去执行当前的方法
                    invocation.Proceed();

                    //存入缓存
                    if (!string.IsNullOrEmpty(redisKey))
                    {
                        object response;
                        var type = invocation.Method.ReturnType;
                        if (typeof(Task).IsAssignableFrom(type))
                        {
                            //获取指定名称的公共属性的对象
                            var resultProperty = type.GetProperty("Result");
                            response = resultProperty.GetValue(invocation.ReturnValue);
                        }
                        else
                        {
                            response = invocation.ReturnValue;
                        }
                        if (response == null) response = string.Empty;

                    //redisContext.SetAsync(redisKey, response, TimeSpan.FromMinutes(cacheAttribute.AbsoluteExpiration));



                    }
                
            }
            else
            {
                invocation.Proceed();
            }

        }

        public void DotRedisMethod(string MethodName, IInvocation invocation) 
        {
            var redisMethod = redisContext.GetType().GetMethod(MethodName, BindingFlags.Instance | BindingFlags.Public);
            object obj = Activator.CreateInstance(redisContext.GetType());
            redisMethod = redisMethod.MakeGenericMethod(invocation.TargetType);
            redisMethod.Invoke(obj, invocation.Arguments);
        }

        /// <summary>
        /// 检查缓存特性
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <returns></returns>
        public CacheAttribute MethodCacheAttributeChacking(MethodInfo methodInfo)
        {
            //对当前方法的特性进行验证
            var cacheAttribute = methodInfo.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CacheAttribute)) as CacheAttribute;
            return cacheAttribute;
        }


        ///// <summary>
        ///// 生成缓存键
        ///// </summary>
        ///// <param name="invocation"></param>
        ///// <returns></returns>
        //public string CustomCacheKey(IInvocation invocation)
        //{
        //    string key = $">";


        //    var methodArguments = invocation.Arguments.Select(GetArgumentValue).ToList();
        //    //获取参数列表
        //    foreach (var item in methodArguments)
        //    {
        //        key += $":{item}";
        //    }
        //    return key.Trim();
        //}

        //protected string GetArgumentValue(object arg)
        //{
        //    if (arg is DateTime || arg is DateTime?)
        //        return ((DateTime)arg).ToString("yyyyMMddHHmmss");
        //    if (arg is null || string.IsNullOrEmpty(arg.ToString()))
        //        return arg.ToString().Trim();
        //    if (arg is not null) 
        //    {
        //        if (arg is Expression)
        //        {
        //            var obj = arg as Expression;
        //            var result = Resolve(obj);
        //            return MD5Helper.MD5Encrypt16(result);

        //        }
        //        else if (arg.GetType().IsClass)
        //        {
        //            return MD5Helper.MD5Encrypt16(JsonConvert.SerializeObject(arg));
        //        }
        //        return $"value:{arg.ToString().Trim()}";
        //    }
        //    return string.Empty;
        //}

        string Resolve(Expression expression)
        {
            ExpressionContext expContext = new ExpressionContext();
            expContext.Resolve(expression, ResolveExpressType.WhereSingle);
            var value = expContext.Result.GetString();
            var pars = expContext.Parameters;
            pars.ForEach(x =>
            {
                value = value.Replace(x.ParameterName, x.Value.ToString().Trim());
            });
            return value;
        }
    }
}
